﻿// Copyright (c) 2007-2020 西安交通信息投资营运有限公司 版权所有
// 作者:吕艳阳

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Reflection;
using XCI.Helper;

namespace XCI.Core
{
    /// <summary>
    /// 模型注解
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ApiModelAttribute : Attribute
    {
        /// <summary>
        /// 说明
        /// </summary>
        public string Caption { get; set; }
    }

    /// <summary>
    /// 模型属性注解
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class ApiModelPropertyAttribute : Attribute
    {
        /// <summary>
        /// 属性说明
        /// </summary>
        public string Caption { get; set; }

        /// <summary>
        /// 是否主键字段
        /// </summary>
        public bool PrimaryKey { get; set; }

        /// <summary>
        /// 是否父主键字段
        /// </summary>
        public bool ParentKey { get; set; }

        /// <summary>
        /// 是否序号字段
        /// </summary>
        public bool PathKey { get; set; }

        /// <summary>
        /// 是否下拉控件显示字段
        /// </summary>
        public bool DisplayMember { get; set; }

        /// <summary>
        /// 是否下拉控件值字段
        /// </summary>
        public bool ValueMember { get; set; }

        /// <summary>
        /// 是否导出字段
        /// </summary>
        public bool Exportable { get; set; } = true;

        /// <summary>
        /// 是否显示字段
        /// </summary>
        public bool Visable { get; set; } = true;

        /// <summary>
        /// 序号
        /// </summary>
        public int Order { get; set; }
    }

    /// <summary>
    /// 模型信息
    /// </summary>
    public class ApiModel
    {
        /// <summary>
        /// 模型类型
        /// </summary>
        public Type Type { get; internal set; }

        /// <summary>
        /// 模型名称
        /// </summary>
        public string Name { get; internal set; }

        /// <summary>
        /// 模型说明
        /// </summary>
        public string Caption { get; internal set; }

        /// <summary>
        /// 模型属性集合
        /// </summary>
        public List<ApiModelPropertyInfo> Propertys { get; internal set; }

        /// <summary>
        /// 主键字段
        /// </summary>
        public ApiModelPropertyInfo PrimaryKey { get; internal set; }

        /// <summary>
        /// 父主键字段
        /// </summary>
        public ApiModelPropertyInfo ParentKey { get; internal set; }

        /// <summary>
        /// 序号字段
        /// </summary>
        public ApiModelPropertyInfo PathKey { get; internal set; }

        /// <summary>
        /// 下拉控件显示字段
        /// </summary>
        public ApiModelPropertyInfo DisplayMember { get; internal set; }

        /// <summary>
        /// 下拉控件值字段
        /// </summary>
        public ApiModelPropertyInfo ValueMember { get; internal set; }
    }

    /// <summary>
    /// 模型属性信息
    /// </summary>
    public class ApiModelPropertyInfo
    {
        /// <summary>
        /// 属性类型
        /// </summary>
        public Type Type { get; internal set; }

        /// <summary>
        /// 属性名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 属性说明
        /// </summary>
        public string Caption { get; set; }

        /// <summary>
        /// 是否主键字段
        /// </summary>
        public bool IsPrimaryKey { get; set; }

        /// <summary>
        /// 是否父主键字段
        /// </summary>
        public bool IsParentKey { get; set; }

        /// <summary>
        /// 是否序号字段
        /// </summary>
        public bool IsPathKey { get; set; }

        /// <summary>
        /// 是否下拉控件显示字段
        /// </summary>
        public bool IsDisplayMember { get; set; }

        /// <summary>
        /// 是否下拉控件值字段
        /// </summary>
        public bool IsValueMember { get; set; }

        /// <summary>
        /// 是否导出字段
        /// </summary>
        public bool Exportable { get; set; }

        /// <summary>
        /// 是否显示字段
        /// </summary>
        public bool Visable { get; set; }

        /// <summary>
        /// 序号
        /// </summary>
        public int Order { get; set; }

        /// <summary>
        /// 列属性
        /// </summary>
        public PropertyInfo Property { get; internal set; }

        /// <summary>
        /// 设置属性值
        /// </summary>
        public void SetValue(object target, object val) { Property.SetValue(target, val); }

        /// <summary>
        /// 获取属性值
        /// </summary>
        public object GetValue(object target) { return Property.GetValue(target); }
    }

    /// <summary>
    /// 模型信息管理器
    /// </summary>
    public static class ApiModelManager
    {
        private static readonly StaticCache<Type, ApiModel> Cache = new StaticCache<Type, ApiModel>();

        /// <summary>
        /// 根据类型获取模型信息
        /// </summary>
        /// <typeparam name="T">模型类型</typeparam>
        /// <returns>返回模型信息</returns>
        public static ApiModel Get<T>()
        {
            return Get(typeof(T));
        }

        /// <summary>
        /// 根据类型获取模型信息
        /// </summary>
        /// <param name="type">模型类型</param>
        /// <returns>返回模型信息</returns>
        public static ApiModel Get(Type type)
        {
            return type == null ? null : Cache.Get(type, () => GetModelData(type));
        }

        /// <summary>
        /// 反射类获取模型信息
        /// </summary>
        /// <param name="type">模型类型</param>
        private static ApiModel GetModelData(Type type)
        {
            var modelAttr = ObjectHelper.GetAttribute<ApiModelAttribute>(type) ?? new ApiModelAttribute();
            var data = new ApiModel
            {
                Type = type,
                Name = type.Name,
                Caption = modelAttr.Caption ?? string.Empty,
                Propertys = new List<ApiModelPropertyInfo>()
            };

            foreach (var pro in type.GetProperties())
            {
                var property = GetModelPropertyData(pro);
                if (property != null)
                {
                    data.Propertys.Add(property);
                }
            }

            data.PrimaryKey = data.Propertys.FirstOrDefault(p => p.IsPrimaryKey);
            data.ParentKey = data.Propertys.FirstOrDefault(p => p.IsParentKey);
            data.PathKey = data.Propertys.FirstOrDefault(p => p.IsPathKey);
            data.DisplayMember = data.Propertys.FirstOrDefault(p => p.IsDisplayMember);
            data.ValueMember = data.Propertys.FirstOrDefault(p => p.IsValueMember);
            return data;
        }

        /// <summary>
        /// 获取指定类型中所有可导出的字段名称集合
        /// </summary>
        /// <param name="type">对象类型</param>
        public static string[] GetEffectiveExportFields(Type type)
        {
            var info = Get(type);
            return info.Propertys.Where(p => p.Exportable).Select(p => p.Caption).ToArray();
        }

        /// <summary>
        /// 反射类属性获取模型属性信息
        /// </summary>
        /// <param name="pinfo">类属性对象</param>
        private static ApiModelPropertyInfo GetModelPropertyData(PropertyInfo pinfo)
        {
            var colAttr = ObjectHelper.GetAttribute<ApiModelPropertyAttribute>(pinfo);
            ApiModelPropertyInfo info;
            if (colAttr != null)
            {
                info = new ApiModelPropertyInfo
                {
                    Type = pinfo.PropertyType,
                    Name = pinfo.Name,
                    Caption = colAttr.Caption,
                    Exportable = colAttr.Exportable,
                    Visable = colAttr.Visable,
                    IsPrimaryKey = colAttr.PrimaryKey,
                    IsParentKey = colAttr.ParentKey,
                    IsPathKey = colAttr.PathKey,
                    IsDisplayMember = colAttr.DisplayMember,
                    IsValueMember = colAttr.ValueMember,
                    Property = pinfo
                };
            }
            else
            {
                info = new ApiModelPropertyInfo
                {
                    Type = pinfo.PropertyType,
                    Name = pinfo.Name,
                    Caption = null,
                    Exportable = false,
                    Visable = true,
                    IsPrimaryKey = false,
                    IsParentKey = false,
                    IsPathKey = false,
                    IsDisplayMember = false,
                    IsValueMember = false,
                    Property = pinfo
                };
            }

            var displayAttr = ObjectHelper.GetAttribute<DisplayAttribute>(pinfo);
            if (string.IsNullOrEmpty(info.Caption) && displayAttr != null)
            {
                info.Caption = displayAttr.Name;
            }
            if (displayAttr != null && displayAttr.GetOrder().HasValue)
            {
                info.Order = displayAttr.Order;
            }
            if (string.IsNullOrEmpty(info.Caption))
            {
                info.Caption = pinfo.Name;
            }
            return info;
        }

        /// <summary>
        /// 把对象集合转为DataTable
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="list">对象集合</param>
        /// <param name="exportFields">指定导出的字段数组</param>
        /// <returns>新创建的DataTable</returns>
        public static DataTable ConvertExportObjectListToDataTable<T>(IEnumerable<T> list, string[] exportFields = null) where T : class
        {
            var modelData = Get<T>();
            DataTable table = new DataTable();
            table.TableName = modelData.Caption;
            if (string.IsNullOrEmpty(table.TableName)) table.TableName = modelData.Name;

            var names = new List<string>();
            foreach (var item in modelData.Propertys)
            {
                if (exportFields != null && !exportFields.Any(p => p.Equals(item.Caption))) continue;
                if (!item.Exportable) continue;
                if (names.Any(p => p.Equals(item.Name))) continue;
                names.Add(item.Name);
                if (item.Property.PropertyType.IsGenericType && item.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    var nullableConverter = new NullableConverter(item.Property.PropertyType);
                    table.Columns.Add(new DataColumn { ColumnName = item.Name, DataType = nullableConverter.UnderlyingType, Caption = item.Caption });
                }
                else
                {
                    table.Columns.Add(new DataColumn { ColumnName = item.Name, DataType = item.Property.PropertyType, Caption = item.Caption });
                }
            }
            foreach (T model in list)
            {
                var row = table.NewRow();
                foreach (DataColumn column in row.Table.Columns)
                {
                    string colName = column.ColumnName;
                    if (string.IsNullOrEmpty(colName)) continue;

                    colName = colName.Trim();
                    var colObject = modelData.Propertys.FirstOrDefault(p => p.Name.Equals(colName));
                    if (colObject == null) continue;

                    object value = colObject.GetValue(model);
                    row[colName] = value;
                }
                table.Rows.Add(row);
            }
            return table;
        }

        /// <summary>
        /// 把DataTable转为对象集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="table">DataTable</param>
        /// <param name="objectFactory">对象创建工厂</param>
        /// <returns>新创建的集合对象</returns>
        public static List<T> ConvertExportDataTableToObjectList<T>(DataTable table, Func<T> objectFactory = null) where T : class
        {
            var modelData = Get<T>();
            var list = new List<T>();
            if (objectFactory == null) objectFactory = ObjectHelper.CreateInstance<T>;

            foreach (DataRow row in table.Rows)
            {
                var model = objectFactory();
                foreach (DataColumn column in row.Table.Columns)
                {
                    if (string.IsNullOrEmpty(column.Caption)) continue;

                    var colObject = modelData.Propertys.FirstOrDefault(p => p.Caption.Equals(column.Caption));
                    if (colObject == null) continue;
                    object value = row[column];
                    try
                    {
                        colObject.SetValue(model, ObjectHelper.ConvertObjectValue(value, colObject.Property.PropertyType));
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"{column.Caption ?? column.ColumnName}的值不正确，无法赋值", ex);
                    }
                }
                list.Add(model);
            }
            return list;
        }
    }
}